﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using BindableDataGrid.Data;
using SilveR.DataValidators;
using SilveR.GUIValidators;

namespace SilveR.Views.StatsViews
{
    public partial class GraphicalAnalysis : UserControl, IAnalysis
    {
        private GraphicalVariables graphicalVariables;

        public GraphicalAnalysis()
        {
            InitializeComponent();

            graphicalVariables = new GraphicalVariables();
            this.DataContext = graphicalVariables;
        }

        private void Target_ItemDragCompleted(object sender, ItemDragEventArgs e)
        {
            //ensure that the available variables stay sorted...
            SortableObservableCollection<ColumnInfo> availableVariables = (SortableObservableCollection<ColumnInfo>)lstAvailableVariables.ItemsSource;
            availableVariables.Sort(new ColumnIndexComparer());

            //only enable the covariate transformation dropdown if it has items
            cboResponseTransformations.IsEnabled = !String.IsNullOrEmpty(graphicalVariables.Response);

            //only enable the covariate transformation dropdown if it has items
            cboXAxisTransformations.IsEnabled = !String.IsNullOrEmpty(graphicalVariables.XAxis);
        }

        private void oneItem_DragOverOrDrop(object sender, Microsoft.Windows.DragEventArgs e)
        {
            ListBoxDragDropTarget target = (ListBoxDragDropTarget)sender;
            ListBox lstBox = (ListBox)target.Content;

            //only allow one item in this listbox
            if (lstBox.Items.Count == 1)
            {
                e.Effects = Microsoft.Windows.DragDropEffects.None;
                e.Handled = true;
            }
        }

        private void cboData_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //refill the available variables box when the selected datahandler changes
            DataHandler dh = (DataHandler)cboData.SelectedItem;

            if (dh != null)
            {
                graphicalVariables.AvailableVariables = dh.GetVariableList();

                ObservableCollection<string> caseIDFactors = new ObservableCollection<string>();

                foreach(ColumnInfo info in dh.GetVariableList())
                {
                    caseIDFactors.Add(info.ColumnName);
                }

                cboCaseIDfactor.ItemsSource = caseIDFactors;

                cboCaseIDfactor.IsEnabled = (caseIDFactors.Count > 0);
            }
        }

        #region IAnalysis Members

        public bool ValidateGUI()
        {
            List<ValidationResult> validationResult = new List<ValidationResult>();
            bool validatedOK = Validator.TryValidateObject(graphicalVariables, new ValidationContext(graphicalVariables, null, null), validationResult, true);

            if (!validatedOK)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(validationResult);
                vmw.Show();
            }

            return validatedOK;
        }

        public event DataValidatedEventHandler DataValidated;

        public void ValidateData()
        {
            GraphicalValidator gpv = new GraphicalValidator(graphicalVariables);
            bool result = gpv.Validate();

            if (gpv.WarningMessages.Count > 0)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(gpv.WarningMessages);
                vmw.Closed += new EventHandler(vmw_Closed);
                vmw.Show();
            }
            else
            {
                if (DataValidated != null)
                    DataValidated(this, new DataValidatedEventArgs(result));
            }
        }

        private void vmw_Closed(object sender, EventArgs e)
        {
            ValidationMessagesWindow vmw = (ValidationMessagesWindow)sender;

            if (DataValidated != null)
                DataValidated(this, new DataValidatedEventArgs(vmw.DialogResult.Value));
        }

        public string[] ExportData()
        {
            DataTable dtNew = this.DataHandler.DataTable.Copy();
            dtNew.Name = "ExportData";

            //Get the response and treatment columns
            foreach (DataColumn col in this.DataHandler.DataTable.Columns)
            {
                if (graphicalVariables.Response != col.ColumnName && graphicalVariables.XAxis != col.ColumnName && graphicalVariables.FirstCatFactor != col.ColumnName && graphicalVariables.SecondCatFactor != col.ColumnName && graphicalVariables.CaseIDFactor != col.ColumnName)
                {
                    dtNew.Columns.Remove(col);
                }
            }

            //ensure that all data is trimmed
            dtNew.TrimAllDataInDataTable();

            //if the response is blank then remove that row
            dtNew.RemoveBlankRow(graphicalVariables.Response);

            //...and export them
            return dtNew.GetCSVArray();
        }

        public DataHandler DataHandler
        {
            get { return graphicalVariables.SelectedDataHandler; }
        }

        public Dictionary<string, string> GetAnalysisArguments()
        {
            Dictionary<string, string> arguments = new Dictionary<string, string>();

            //X-Axis variable
            arguments.Add("X-Axis", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(graphicalVariables.XAxis))); //4

            //X-Axis transformation
            arguments.Add("X-Axis Transform", "\"" + graphicalVariables.XAxisTransformation + "\""); //5

            //Response variable
            arguments.Add("Response", ArgumentConverters.ConvertIllegalChars(graphicalVariables.Response)); //6

            //Response transformation
            arguments.Add("Response Transform", "\"" + graphicalVariables.ResponseTransformation + "\""); //7

            //1st cat factor
            arguments.Add("First Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(graphicalVariables.FirstCatFactor))); //8

            //2nd cat factor
            arguments.Add("Second Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(graphicalVariables.SecondCatFactor))); //9

            //Graph type
            if (graphicalVariables.OverlaidSelected)
            {
                arguments.Add("Graph Type", "Overlaid"); //10
            }
            else
            {
                arguments.Add("Graph Type", "Separate");
            }

            //Main title
            arguments.Add("Main Title", ArgumentConverters.GetNULLOrText(graphicalVariables.MainTitle)); //11

            //X-Axis title
            arguments.Add("X-Axis Title", ArgumentConverters.GetNULLOrText(graphicalVariables.XAxisTitle)); //12

            //Y Axis title
            arguments.Add("Y Axis Title", ArgumentConverters.GetNULLOrText(graphicalVariables.YAxisTitle)); //13

            //Scatterplot
            arguments.Add("Scatterplot", ArgumentConverters.GetYesOrNo(graphicalVariables.ScatterplotSelected)); //14

            //Linear plot
            arguments.Add("Linear Fit", ArgumentConverters.GetYesOrNo(graphicalVariables.LinearFitSelected)); //15

            //Correlation and p-value
            arguments.Add("Correlation P Value", ArgumentConverters.GetYesOrNo(graphicalVariables.CorrelationPValueSelected)); //16

            //Boxplot
            arguments.Add("Boxplot", ArgumentConverters.GetYesOrNo(graphicalVariables.BoxplotSelected)); //17

            //Outliers
            arguments.Add("Outliers", ArgumentConverters.GetYesOrNo(graphicalVariables.OutliersSelected)); //18

            //SEM Plot
            arguments.Add("SEM Plot", ArgumentConverters.GetYesOrNo(graphicalVariables.SEMPlotSelected)); //19

            //Column plot type
            if (graphicalVariables.ColumnPlotSelected) //20
            {
                arguments.Add("Plot Type", "ColumnPlot");
            }
            else
            {
                arguments.Add("Plot Type", "LinePlot");
            }

            //Histogram plot
            arguments.Add("Histogram", ArgumentConverters.GetYesOrNo(graphicalVariables.HistogramSelected)); //21

            //Normal distribution fit
            arguments.Add("Normal Dist Fit", ArgumentConverters.GetYesOrNo(graphicalVariables.NormalDistSelected)); //22

            //Case profiles plot
            arguments.Add("Case Profiles Plot", ArgumentConverters.GetYesOrNo(graphicalVariables.CaseProfilesPlotSelected)); //23

            //Case ID Factor
            arguments.Add("Case ID Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(graphicalVariables.CaseIDFactor))); //24

            //Reference Line
            arguments.Add("Reference Line", ArgumentConverters.GetNULLOrText(graphicalVariables.ReferenceLine)); //25

            //Legend
            arguments.Add("Legend", ArgumentConverters.GetYesOrNo(graphicalVariables.DisplayLegend)); //26

            return arguments;
        }

        public string AnalysisName
        {
            get { return "Graphical Analysis"; }
        }

        public string ScriptName
        {
            get { return "GraphicalAnalysis.R"; }
        }

        public void UpdateDataHandlers()
        {
            //remember the current data handler before updating the available variables so that it can be selected later...
            DataHandler currentDataHandler = graphicalVariables.SelectedDataHandler;

            App app = (App)Application.Current;
            graphicalVariables.AvailableDataHandlers = app.DataHandlers;

            //reselect the current handler (if any)
            graphicalVariables.SelectedDataHandler = currentDataHandler;
        }

        #endregion
    }

    public class GraphicalVariables : INotifyPropertyChanged
    {
        public GraphicalVariables()
        {
            App app = (App)Application.Current;
            availableDataHandlers = app.DataHandlers;
        }

        private ObservableCollection<DataHandler> availableDataHandlers = new ObservableCollection<DataHandler>();
        public ObservableCollection<DataHandler> AvailableDataHandlers
        {
            get
            {
                return availableDataHandlers;
            }

            set
            {
                availableDataHandlers = value;
                NotifyPropertyChanged("AvailableDataHandlers");
            }
        }

        private DataHandler selectedDataHandler;
        public DataHandler SelectedDataHandler
        {
            get { return selectedDataHandler; }
            set
            {
                selectedDataHandler = value;
                NotifyPropertyChanged("SelectedDataHandler");
            }
        }

        private ObservableCollection<ColumnInfo> availableVariables = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> AvailableVariables
        {
            get { return availableVariables; }
            set
            {
                availableVariables = value;
                NotifyPropertyChanged("AvailableVariables");
            }
        }

        private ObservableCollection<ColumnInfo> responseList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> ResponseList
        {
            get { return responseList; }
            set
            {
                responseList[0] = value[0];
                NotifyPropertyChanged("ResponseList");
            }
        }

        [CustomValidation(typeof(GraphicalAnalysisGUIValidator), "CheckStringVarSet")]
        [Display(Name = "Response")]
        public string Response
        {
            get
            {
                if (responseList.Count == 1)
                    return responseList[0].ColumnName;
                else
                    return null;
            }
        }

        private string responseTransformation = "None";
        public string ResponseTransformation
        {
            get { return responseTransformation; }
            set { responseTransformation = value; }
        }

        private List<string> transformationList = new List<string>() { "None", "Log10", "Loge", "Square Root", "ArcSine" };
        public List<string> TransformationList
        {
            get { return transformationList; }
        }


        private ObservableCollection<ColumnInfo> xAxisList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> XAxisList
        {
            get { return xAxisList; }
            set
            {
                xAxisList[0] = value[0];
                NotifyPropertyChanged("XAxisList");
            }
        }

        [CustomValidation(typeof(GraphicalAnalysisGUIValidator), "ValidateXAxis")]
        public string XAxis
        {
            get
            {
                if (xAxisList.Count == 1)
                    return xAxisList[0].ColumnName;
                else
                    return null;
            }
        }

        private string xAxisTransformation = "None";
        public string XAxisTransformation
        {
            get { return xAxisTransformation; }
            set { xAxisTransformation = value; }
        }

        private ObservableCollection<ColumnInfo> firstCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> FirstCatFactorList
        {
            get { return firstCatFactorList; }
            set
            {
                firstCatFactorList = value;
                NotifyPropertyChanged("FirstCatFactorList");
            }
        }

        public string FirstCatFactor
        {
            get
            {
                if (firstCatFactorList.Count == 1)
                    return firstCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> secondCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> SecondCatFactorList
        {
            get { return secondCatFactorList; }
            set
            {
                secondCatFactorList = value;
                NotifyPropertyChanged("SecondCatFactorList");
            }
        }

        public string SecondCatFactor
        {
            get
            {
                if (secondCatFactorList.Count == 1)
                    return secondCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private bool overlaidSelected = true;
        public bool OverlaidSelected
        {
            get { return overlaidSelected; }
            set {
                overlaidSelected = value;
                NotifyPropertyChanged("OverlaidSelected");
            }
        }

        private bool separateSelected;
        public bool SeparateSelected
        {
            get { return separateSelected; }
            set
            {
                separateSelected = value;
                NotifyPropertyChanged("SeparateSelected");
            }
        }

        private bool displayLegend;
        public bool DisplayLegend
        {
            get { return displayLegend; }
            set
            {
                displayLegend = value;
                NotifyPropertyChanged("DisplayLegend");
            }
        }

        private string mainTitle;
        public string MainTitle
        {
            get { return mainTitle; }
            set
            {
                mainTitle = value;
                NotifyPropertyChanged("MainTitle");
            }
        }

        private string xAxisTitle;
        public string XAxisTitle
        {
            get { return xAxisTitle; }
            set
            {
                xAxisTitle = value;
                NotifyPropertyChanged("XAxisTitle");
            }
        }

        private string yAxisTitle;
        public string YAxisTitle
        {
            get { return yAxisTitle; }
            set
            {
                yAxisTitle = value;
                NotifyPropertyChanged("YAxisTitle");
            }
        }

        private bool scatterplotSelected;
        public bool ScatterplotSelected
        {
            get { return scatterplotSelected; }
            set
            {
                scatterplotSelected = value;
                NotifyPropertyChanged("ScatterplotSelected");
            }
        }

        private bool linearFitSelected;
        public bool LinearFitSelected
        {
            get { return linearFitSelected; }
            set
            {
                linearFitSelected = value;
                NotifyPropertyChanged("LinearFitSelected");
            }
        }

        private bool correlationPValueSelected;
        public bool CorrelationPValueSelected
        {
            get { return correlationPValueSelected; }
            set
            {
                correlationPValueSelected = value;
                NotifyPropertyChanged("CorrelationPValueSelected");
            }
        }

        private bool boxplotSelected;
        public bool BoxplotSelected
        {
            get { return boxplotSelected; }
            set
            {
                boxplotSelected = value;
                NotifyPropertyChanged("BoxplotSelected");
            }
        }

        private bool outliersSelected;
        public bool OutliersSelected
        {
            get { return outliersSelected; }
            set
            {
                outliersSelected = value;
                NotifyPropertyChanged("OutliersSelected");
            }
        }

        private bool semPlotSelected;
        public bool SEMPlotSelected
        {
            get { return semPlotSelected; }
            set
            {
                semPlotSelected = value;
                NotifyPropertyChanged("SEMPlotSelected");
            }
        }

        private bool columnPlotSelected = true;
        public bool ColumnPlotSelected
        {
            get { return columnPlotSelected; }
            set
            {
                columnPlotSelected = value;
                NotifyPropertyChanged("ColumnPlotSelected");
            }
        }

        private bool linePlotSelected;
        public bool LinePlotSelected
        {
            get { return linePlotSelected; }
            set
            {
                linePlotSelected = value;
                NotifyPropertyChanged("LinePlotSelected");
            }
        }

        private bool histogramSelected;
        public bool HistogramSelected
        {
            get { return histogramSelected; }
            set
            {
                histogramSelected = value;
                NotifyPropertyChanged("HistogramSelected");
            }
        }

        private bool normalDistSelected;
        public bool NormalDistSelected
        {
            get { return normalDistSelected; }
            set
            {
                normalDistSelected = value;
                NotifyPropertyChanged("NormalDistSelected");
            }
        }

        private bool caseProfilesPlotSelected;
        public bool CaseProfilesPlotSelected
        {
            get { return caseProfilesPlotSelected; }
            set
            {
                caseProfilesPlotSelected = value;
                NotifyPropertyChanged("CaseProfilesPlotSelected");
            }
        }

        private string caseIDFactor = null;

        [CustomValidation(typeof(GraphicalAnalysisGUIValidator), "ValidateCaseIDFactor")]
        public string CaseIDFactor
        {
            get { return caseIDFactor; }
            set
            {
                caseIDFactor = value;
                NotifyPropertyChanged("CaseIDFactor");
            }
        }

        private string referenceLine = null;
        public string ReferenceLine
        {
            get { return referenceLine; }
            set
            {
                referenceLine = value;
                NotifyPropertyChanged("ReferenceLine");
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event, passing the source property that is being updated.
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}